package predatorprey;
import java.util.*;

/**
 *
 * @author Kenneth Aamås, Vegard Hovlid, Eirik Korsnes.
 */
public class Lynx extends Animal
{
    // Characteristics shared by all lynx (class variables).
    
    // The age at which a lynx can start to breed.
 
    private static final int BREEDING_AGE = 50;
    // The age to which a lynx can live.

    private static final int MAX_AGE = 1000;

    // The likelihood of a lynx breeding.

    private static final double BREEDING_PROBABILITY = 0.20;

    // The maximum number of births.
    private static final int MAX_LITTER_SIZE = 2;
    // The food value of a single hare. In effect, this is the
    // number of steps a lynx can go before it has to eat again.

    private static final int HARE_FOOD_VALUE = 50;

    // Whether or not an animal is close to a partner of the opposite sex

    private boolean hasPartner;
    // Whether the animal is a female or not.
    private boolean female;
    
    private static Random rnd = new Random();
    
    /**
     * Create a lynx. A lynx can be created as a new born (age zero
     * and not hungry) or with a random age and food level.
     * 
     * @param randomAge If true, the lynx will have random age and hunger level.
     * @param field The field currently occupied.
     * @param location The location within the field.
     */
    public Lynx(boolean randomAge ,Field field, Location location)
    {
        super(field, location);
        this.hasPartner = false;
        female = rnd.nextBoolean();
        if(randomAge)
        {
            age = rnd.nextInt(MAX_AGE);
            hunger = rnd.nextInt(HARE_FOOD_VALUE);
        }
        else {
            age = 0;
            hunger = HARE_FOOD_VALUE;
        }
    }
    
    @Override
    public void act(List<Agent> newLynx)
    {
        incrementAge();
        super.incrementHunger();
        if(isAlive())
        {
            if(female == true);
            {
                giveBirth(newLynx);
            }
            // Move towards a source of food if found.
            Location newLocation = findFood();
            if(newLocation == null)
            {
                // No food found - try to move to a free location.
                newLocation = getField().freeAdjacentLocation(getLocation());
            }
            // See if it was possible to move.
            if(newLocation != null)
            {
                setLocation(newLocation);
            }
            else
            {
                // Overcrowding.
                setDead();
            }
        }
    }
    
    /**
     * Increase the age. This could result in the lynx's death.
     */
    private void incrementAge()
    {
        age++;
        if(age > MAX_AGE)
        {
            setDead();
        }
    }
    
    /**
     * Look for hares adjacent to the current location.
     * Only the first live hare is eaten.
     * @return Where food was found, or null if it wasn't.
     */
    private Location findFood()
    {
        field = getField();
        List<Location> adjacent = field.adjacentLocations(getLocation());
        Iterator<Location> it = adjacent.iterator();
        while(it.hasNext()) 
        {
            Location where = it.next();
            Object animal = field.getObjectAt(where);
            if(animal instanceof Hare) 
            {
                Hare hare = (Hare) animal;
                if(hare.isAlive()) 
                { 
                    hare.setDead();
                    hunger = HARE_FOOD_VALUE;
                    return where;
                }
            }
        }
        return null;
    }
    
    /**
     * A lynx can breed if it has reached the breeding age.
     */
    private boolean canBreed()
    {
        return age >= BREEDING_AGE;
    }
    
    /**
     * Generate a number representing the number of births,
     * if it can breed.
     * @return The number of births (may be zero).
     */
    private int breed()
    {
        int births = 0;
        if(canBreed() && rnd.nextDouble() <= BREEDING_PROBABILITY) 
        {
            births = rnd.nextInt(MAX_LITTER_SIZE) + 1;
        }
        return births;
    }
    
    /**
     * Check whether or not this lynx is to give birth at this step.
     * New births will be made into free adjacent locations.
     * @param newLynx A list to return newly born lynx.
     */
    private void giveBirth(List<Agent> newLynx)
    {
        // New lynx are born into adjacent locations.
        // Get a list of adjacent free locations.
        findPartner();
        if(hasPartner == true)
        {
            field = getField();
            List<Location> free = field.getFreeAdjacentLocations(getLocation());
            int births = breed();
            for(int b = 0; b < births && free.size() > 0; b++) 
            {
                Location loc = free.remove(0);
                Lynx young = new Lynx(false, field, loc);
                newLynx.add(young);
            }
        }
        hasPartner = false;
    }
    
    private void findPartner() {
        field = getField();
        List<Location> adjecent = field.adjacentLocations(getLocation());
        Iterator<Location> it = adjecent.iterator();
        while (it.hasNext()) {
            Location where = it.next();
            Object animal = field.getObjectAt(where);
            if (animal instanceof Lynx) {
                Lynx lynx = (Lynx) animal;
                if (lynx.isAlive() && lynx.female == false && lynx.canBreed()) {
                    hasPartner = true;
                }
            }
        }
    }
    
}